#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (C) 2023 reinovo, Inc. All Rights Reserved 
#
# @Time    : 2024/7/30 下午3:31
# @Author  : hmm
# @Email   : liuyuhang0531@foxmail.com
# @File    : test2.py
# @Software: PyCharm


#导入程序运行必须模块
import sys
import time

#PyQt5中使用的基本控件都在PyQt5.QtWidgets模块中
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QThread,pyqtSignal
import serial.tools.list_ports
import serial
import modbus_tk.defines as cst
from modbus_tk import modbus_rtu
import struct
#导入designer工具生成的login模块
from rei_UI import Ui_Form


class MyMainForm(QMainWindow, Ui_Form):
    def __init__(self, parent=None):
        super(MyMainForm, self).__init__(parent)
        self.port_is = False
        self.port_is_open = False
        self.setupUi(self)
        self.get_prots()
        self.pushButton_3.clicked.connect(self.get_prots)
        self.pushButton.clicked.connect(self.connect_port)
        self.get_data = WorkThread()
        self.send_data = WorkThread()
        self.pushButton_2.clicked.connect(self.close_port)
        self.pushButton_4.clicked.connect(self.get_execute)
        self.pushButton_5.clicked.connect(self.send_execute)
        self.pushButton_6.clicked.connect(self.reset_output)
        self.pushButton_7.clicked.connect(self.set_IO_1)
        self.pushButton_8.clicked.connect(self.set_IO_4)
        self.pushButton_9.clicked.connect(self.set_IO_3)
        self.pushButton_10.clicked.connect(self.set_IO_2)
        self.pushButton_11.clicked.connect(self.cb_extra_motor_1)
        self.pushButton_12.clicked.connect(self.cb_extra_motor_2)
        self.pushButton_13.clicked.connect(self.cb_extra_motor_stop)
        self.pushButton_14.clicked.connect(self.cb_set_relay)
        self.output_io_1 = 0
        self.output_io_2 = 0
        self.output_io_3 = 0
        self.output_io_4 = 0
        self.relay_state = 0
        self.extra_motor = 0

    def cb_set_relay(self):
        if self.relay_state:
            self.relay_state = 0
        else:
            self.relay_state = 1
        self.set_relay(self.relay_state)

    def cb_extra_motor_1(self):
        self.set_extra_motor(1)
        self.extra_motor = 1

    def cb_extra_motor_2(self):
        self.set_extra_motor(-1)
        self.extra_motor = -1

    def cb_extra_motor_stop(self):
        self.set_extra_motor(0)
        self.extra_motor = 0

    def set_IO_1(self):
        if self.output_io_1:
            self.output_io_1 = 0
        else:
            self.output_io_1 = 1
        return self.set_io()

    def set_IO_2(self):
        if self.output_io_2:
            self.output_io_2 = 0
        else:
            self.output_io_2 = 1
        return self.set_io()

    def set_IO_3(self):
        if self.output_io_3:
            self.output_io_3 = 0
        else:
            self.output_io_3 = 1
        return self.set_io()

    def set_IO_4(self):
        if self.output_io_4:
            self.output_io_4 = 0
        else:
            self.output_io_4 = 1
        return self.set_io()

    def reset_output(self):
        self.horizontalSlider.setValue(0)
        self.horizontalSlider_2.setValue(0)
        self.horizontalSlider_3.setValue(0)
        self.horizontalSlider_4.setValue(0)

    def get_prots(self):
        self.log_info("刷新串口")
        self.comboBox.clear()
        ports_list = list(serial.tools.list_ports.comports())
        if len(ports_list) == 0:
            # self.comboBox.addItem("未识别串口")
            self.log_info("未识别到串口")
            return 0
        self.comboBox.addItem(" ")
        for port in ports_list:
            self.log_info("识别到串口:"+str(port))
            self.comboBox.addItem(str(port).split(" ")[0])
        return 0

    def connect_port(self):
        try:
            port = str(self.comboBox.currentText())
            self.master = modbus_rtu.RtuMaster(serial.Serial(port=port, baudrate=115200, bytesize=8, parity='N', stopbits=1, xonxoff=0))
            self.master.set_timeout(1.0)
            self.log_info("串口连接成功")
            self.port_is_open = True
        except Exception as excpt:
            self.log_info("串口连接失败")
            self.log_info(str(excpt))

    def close_port(self):
        try:
            if self.port_is_open == True:
                self.master.close()
                self.send_data.working = False
                self.get_data.working = False
                self.port_is_open = False
                self.log_info("串口已关闭")
            else:
                self.log_info("串口未连接")
        except Exception as excpt:
            self.log_info("关闭串口失败")
            self.log_info(str(excpt))

    def get_execute(self):
        try:
            if self.port_is_open == True:
                self.get_data.start()
                self.get_data.working = True
                self.get_data.trigger.connect(self.get_CarDate)
                self.log_info("开始获取串口信息")
            else:
                self.log_info("串口未连接")
        except Exception as excpt:
            self.log_info("获取串口信息失败")
            self.log_info(str(excpt))


    def send_execute(self):
        try:
            if self.port_is_open == True:
                self.send_data.start()
                self.send_data.working = True
                # self.get_data.working = False
                self.send_data.trigger.connect(self.send_CarData)
                self.log_info("开始发送串口数据")
            else:
                self.log_info("串口未连接")
        except Exception as excpt:
            self.log_info("串口数据发送失败")
            self.log_info(str(excpt))


    def get_CarDate(self):
        try:
            res_tuple = self.master.execute(1, cst.READ_INPUT_REGISTERS, 0, 27)
            sleep = []
            for i in range(0, 16, 4):
                date = struct.pack('HHHH',
                                   res_tuple[i + 3],
                                   res_tuple[i + 2],
                                   res_tuple[i + 1],
                                   res_tuple[i])
                sleep.append(struct.unpack('d', date)[0])
            self.textBrowser_2.setText(str(sleep[0]))
            self.textBrowser_3.setText(str(sleep[1]))
            self.textBrowser_4.setText(str(sleep[2]))
            self.textBrowser_5.setText(str(sleep[3]))
            self.textBrowser_6.setText(str(res_tuple[19]))
            self.textBrowser_8.setText(str(res_tuple[20]))
            self.textBrowser_7.setText(str(res_tuple[21]))
            temperature = struct.pack('HH',
                               res_tuple[24],
                               res_tuple[23])
            temperature = struct.unpack('f',temperature)
            self.textBrowser_9.setText(str(temperature[0]))
            humidity = struct.pack('HH',
                                   res_tuple[26],
                                   res_tuple[25])
            humidity = struct.unpack('f',humidity)
            self.textBrowser_10.setText(str(humidity[0]))
            input_io = bin(res_tuple[18])[2:].zfill(4)[::-1]
            self.textBrowser_11.setText(str(input_io))
            self.textBrowser_13.setText(str(res_tuple[16]))
            self.textBrowser_12.setText(str(res_tuple[22]))
        except Exception as excpt:
            self.log_info(str(excpt))


    def send_CarData(self):
        try:
            speel_1 = self.horizontalSlider.value()
            speel_2 = self.horizontalSlider_2.value()
            speel_3 = self.horizontalSlider_3.value()
            speel_4 = self.horizontalSlider_4.value()
            self.textBrowser_14.setText(str(speel_1))
            self.textBrowser_15.setText(str(speel_2))
            self.textBrowser_16.setText(str(speel_3))
            self.textBrowser_17.setText(str(speel_4))
            speed = [speel_1, speel_2, speel_3, speel_4]
            send_data = []
            for i in range(0, 4):
                date = struct.pack('d', speed[i])
                data = struct.unpack('HHHH', date)
                send_data.append(data[3])
                send_data.append(data[2])
                send_data.append(data[1])
                send_data.append(data[0])
            res = self.master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, 0, 16, output_value=send_data)
        except Exception as excpt:
            self.log_info(str(excpt))

    def set_extra_motor(self, state):
        """
        设置额外电机的状态
        :param state: 状态值（<0:反转, >0:正转, 0:停止）
        :return: 0表示成功, -1表示失败
        """
        if not self.port_is_open:
            self.log_info("串口未连接")
            return -1
        # 根据state确定命令值
        state_text = "停止"
        if state < 0:
            command = 1  # 反转
            state_text = "反转"
        elif state > 0:
            command = 2  # 正转
            state_text = "正转"
        else:
            command = 0  # 停止
        try:
            # 写入单个保持寄存器（功能码06）
            # 地址使用DataAdress.ExtraMotor，这里假设为0x0002（需替换为实际地址）
            response = self.master.execute(
                slave=1,  # 从机地址，根据实际设备调整
                function_code=cst.WRITE_SINGLE_REGISTER,
                starting_address=19,
                output_value=command
            )
            self.log_info("成功设置外接电机状态为:"+state_text)
            return 0
        except Exception as e:
            print(f"外接电机控制命令写入失败: {e}")
            return -1

    def set_relay(self, state):
        """
        设置继电器状态
        :param state: 布尔值（True表示吸合，False表示断开）
        :return: 0表示成功，-1表示失败
        """
        if not self.port_is_open:
            return -1
        try:
            # 写入继电器控制寄存器（功能码：写入单个寄存器）
            response = self.master.execute(
                slave=1,  # 从机地址，根据实际设备调整
                function_code=cst.WRITE_SINGLE_REGISTER,
                starting_address=17,  # 继电器寄存器地址
                output_value=state
            )
            if state:
                self.log_info("设置继电器状态为闭合")
            else:
                self.log_info("设置继电器状态为打开")
            return 0
        except Exception as e:
            print(f"继电器控制命令写入失败: {e}")
            return -1

    def set_io(self):
        """
        设置IO输出
        :param io: 包含7个元素的列表，对应C++中的uint16_t io[7]
        :return: 0表示成功，-1表示失败
        """
        try:
            if self.port_is_open == True:
                io = [self.output_io_1,self.output_io_2,self.output_io_3,self.output_io_4,0,0,0]
                if len(io) != 7:
                    return -1  # 输入参数长度不正确
                self.log_info("开始DO数据发送")
                data = 0
                for i in range(7):
                    # 模拟uint16_t的移位操作，确保值为非负整数
                    data += (int(io[i]) & 0xFFFF) << i
                
                # 等待指定毫秒数
                time.sleep(0.5)
                
                # 写入单个寄存器（地址假设为DataAdress.OutputIO，这里用常量100举例）
                # 注意：pymodbus的write_register返回的是一个响应对象
                response = self.master.execute(
                    slave=1,  # 从机地址，根据实际设备调整
                    function_code=cst.WRITE_SINGLE_REGISTER,
                    starting_address=18,  # 继电器寄存器地址
                    output_value=data
                )
                self.log_info("")
                
            else:
                self.log_info("串口未连接")
        except Exception as excpt:
            self.log_info("设置digital output失败")


    def log_info(self,text):
        self.textBrowser.setText(time.ctime()+"【info】"+text)

class WorkThread(QThread):
    # 自定义信号对象。参数str就代表这个信号可以传一个字符串
    trigger = pyqtSignal(str)

    def __init__(self):
        # 初始化函数
        super(WorkThread, self).__init__()
        self.working = True
        self.num = 0

    # def __del__(self):
    #     self.working = False
    #     self.wait()

    def run(self):
        #重写线程执行的run函数
        #触发自定义信号
        while self.working == True:
            self.num += 1
            file_str = "File index{0}".format(self.num)
            self.trigger.emit(file_str)
            time.sleep(0.2)


if __name__ == "__main__":
    #固定的，PyQt5程序都需要QApplication对象。sys.argv是命令行参数列表，确保程序可以双击运行
    app = QApplication(sys.argv)
    #初始化
    myWin = MyMainForm()
    #将窗口控件显示在屏幕上
    myWin.show()
    #程序运行，sys.exit方法确保程序完整退出。
    sys.exit(app.exec_())